package com.itservice.supporter_navi;

import java.util.ArrayList;
import java.util.Date;

import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
import android.util.Log;

import com.google.android.maps.GeoPoint;
import com.itservice.supporter_navi.MarathonDataBase.MarathonDataBase;
import com.itservice.supporter_navi.MarathonDataBase.libs.Convention;
import com.itservice.supporter_navi.MarathonDataBase.libs.DBUtilities;
import com.itservice.supporter_navi.MarathonDataBase.libs.MarathonCourseData;
import com.itservice.supporter_navi.utilities.CommonMethod;
import com.itservice.supporter_navi.utilities.CommonUtilities;

//=================================================================
// GPSを取得し続けるサービス
//=================================================================
public class GPSManagerService extends Service implements LocationListener{

	public double latitude=0;
	public double longitude=0;

	private boolean modeGps=false;
	private boolean gpsStatus=false;

	private boolean ng_flg = false;
	private double old_distance = 0;
	private int old_section =0;

	private MarathonDataBase marathonDB; // データベースクラス

	//GeoPoint格納用
	GeoPoint gp_S;
	GeoPoint gp_E;
	GeoPoint gp_P;

	private MarathonCourseData courseData;

	// コース区間番号記憶用
	private int section = 0;



	LocationManager mLocationManager;

	// コンストラクタ
	public GPSManagerService()
	{
		// データベースクラス取得
		marathonDB = MarathonDataBase.getInstance();
//		marathonDB = new MarathonDataBase(this);
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}


	// サービス初回起動時処理
	@Override
	public void onCreate() {
		Log.i(CommonUtilities.LOG_TAG, "GPSサービス初回起動");
//		new Thread(new Runnable() {
//	        @Override
//	        public void run() {
//				mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
//				Looper looper = Looper.getMainLooper();
//				mLocationManager.requestLocationUpdates(
//						LocationManager.GPS_PROVIDER,
//						CommonUtilities.LOCATION_MIN_TIME,
//						CommonUtilities.LOCATION_MIN_DISTANCE,
//						GPSManagerService.this,
//						looper);
//				// マラソンマップデータの取得
//
//	        }
//		}).start();
		startGps();
		courseData = MarathonCourseData.getInstance(getResources());
		// 大会コースデータリストを取得
        ArrayList<Convention> conventionList = courseData.GetConventionList();

        // 大会コースを設定
        // ダミーデータ、今回は先頭(大阪マラソン)を設定する。
        Boolean ret = courseData.SetConvention(conventionList.get(CommonUtilities.COURSE_OSAKA_MARATHON).No);

        // サービス起動時に初期化
        marathonDB.setText(DBUtilities.FRIEND_LATITUDE,"0.0");
		marathonDB.setText(DBUtilities.FRIEND_LONGITUDE,"0.0");
		marathonDB.setInteger(DBUtilities.FRIEND_SECTION, -1);
	}

	// 再度サービスが呼ばれた時の処理
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(CommonUtilities.LOG_TAG, "GPSサービス通常起動");
		Log.i(CommonUtilities.LOG_TAG, "緯度:"+ latitude + "経度:" + longitude);
		getLocationData();
		return START_STICKY;
	}

	// サービス終了時の処理
	@Override
	public void onDestroy() {
		Log.i(CommonUtilities.LOG_TAG, "GPSサービス終了");
	}

	// 位置情報が変更された時に呼び出される
	@Override
	public void onLocationChanged(Location location) {
		int LastPoint = courseData.GetCourseMaxPointNum();
		LastPoint--;

		if(LastPoint != section){
			//緯度の取得
			latitude = (location.getLatitude());
			//経度の取得
			longitude = (location.getLongitude());

			//===================================================================
			// 5分間一度も位置情報の更新が無かった場合は一番近いレーンを検索する
			//===================================================================
			// 前回取得GPSを取得できた時間を取得する(初回はランナーモードになった時間)
//			long lastgetgpstime = marathonDB.getLong(DBUtilities.GPS_LAST_GET_TIME, 0);
//
//			// 前回GPS取得時間
//			if(lastgetgpstime != 0){
//				// 現在時刻取得
//				long nowMillisecond = new Date().getTime();
//				// 現在時刻から前回取得時間を引いて秒数に変換して比較を行い、5分以上経過していた時
//				if ((((nowMillisecond - lastgetgpstime) / 1000) > CommonUtilities.GPS_NG_TIME) || ng_flg==true) {
//					// 最寄りのポイントまでの距離
//					double Nearest=0;
//					// 最寄りのセクション番号
//					int NearSection = 0;
//					// 全ポイント検索
//					for(int i = 0 ; i < LastPoint;i++){
//						GeoPoint point= courseData.GetCourseData(i);
//
//						double distance = CommonMethod.DistanceOfBetweenTwoPoints(latitude, longitude, point.getLatitudeE6(), point.getLongitudeE6());
//						if(i==0){
//							Nearest = distance;
//							NearSection = i;
//						}else if(Nearest > distance){
//							Nearest = distance;
//							NearSection = i;
//						}
//					}
//					if(ng_flg==true){
//						if(old_section == NearSection){
//
//						}else{
//
//						}
//
//					}else if(ng_flg==false){
//						old_section = NearSection;
//						old_distance = Nearest;
//						ng_flg = true;
//					}
//					marathonDB.setLong(DBUtilities.GPS_LAST_GET_TIME, nowMillisecond);
//				}
//			}

			// 5分以上経過した場合は処理をいったん中止
			if(ng_flg != true){
				// ダミークラスからGeoPoint値を取得する
				gp_S = courseData.GetCourseData(section);
				gp_E = courseData.GetCourseData(section + 1);
				gp_P = new GeoPoint((int)(latitude*1E6), (int)(longitude*1E6));

				Log.i(CommonUtilities.LOG_TAG,"gp_S:" + gp_S + "gp_E:" + gp_E + "gp_P:"+ gp_P);
				Log.i(CommonUtilities.LOG_TAG,"GPS取得方法:"+ modeGps);

				// GPS取得値補正処理
				/* 値を取得して表示します。 */
				String latlon = CommonMethod.resultPosition(gp_S, gp_E, gp_P);

				// Stringから緯度経度を抜き出す
				String[] array_msg = latlon.split(",");

				latitude = Double.parseDouble(array_msg[0]);
				longitude = Double.parseDouble(array_msg[1]);

				/*******************************************************************/
		        // コーナーから10m以内なら、次の区間の開始とするで表示する
		        /* 値を取得して表示します。 */
		        int DistancePtoE = CommonMethod.distance_atob(latitude, longitude, courseData.GetCourseDataLatitude(section + 1), courseData.GetCourseDataLangitude(section + 1));
				if(10 >= DistancePtoE){
					section++;
				}
				/*******************************************************************/
				/***********************************************************************************/
				// Iwakura 0630 追加 ランナー位置がコース上に乗っていない場合の補正処理
				// ここから
				// 現在いる区間のスタート位置からゴール位置までの距離を求める
				int DistanceStoE = CommonMethod.distance_atob(courseData.GetCourseDataLatitude(section), courseData.GetCourseDataLangitude(section), courseData.GetCourseDataLatitude(section + 1), courseData.GetCourseDataLangitude(section + 1));
				// 現在位置～ゴール位置の距離が区間長より長い場合、ランナー表示はコース上に乗らない為、現在位置を区間スタート位置に補正する
				if (DistancePtoE > DistanceStoE){
					latitude = courseData.GetCourseDataLatitude(section);
					longitude = courseData.GetCourseDataLangitude(section);
				}
				// ここまで 0630 Iwakura
				/***********************************************************************************/

				//=====================================================================
				// 位置情報永久化
				//=====================================================================
				marathonDB.setText(DBUtilities.FRIEND_LATITUDE,Double.toString(latitude));
				marathonDB.setText(DBUtilities.FRIEND_LONGITUDE,Double.toString(longitude));
				marathonDB.setInteger(DBUtilities.FRIEND_SECTION, section);
			}
		}else if(LastPoint == section){
			marathonDB.setBoolean(DBUtilities.FRIEND_GOAL, true);
			Context context = getApplicationContext();
			generateNotificationGoal(context,"ゴールです！おめでとう御座います！");
		}
	}

	// ゴールした時のNotification
	@SuppressLint("NewApi")
	private static void generateNotificationGoal(Context context, String message) {
    	Intent notificationIntent = new Intent(context, MainActivity.class);
    	PendingIntent pendingIntent =
    			PendingIntent.getActivity(context, 0, notificationIntent, 0);

    	//Notification.Builder builder = new Notification.Builder(context);
    	Builder builder = new NotificationCompat.Builder(context);

    	builder.setTicker("ランナーモード中");
    	builder.setContentTitle("マラソン応援アプリ：ランナーモード");
       	builder.setContentText(message);
       	builder.setContentInfo("info");
       	builder.setWhen(System.currentTimeMillis());
       	builder.setContentIntent(pendingIntent);
       	builder.setSmallIcon(android.R.drawable.ic_menu_info_details);
       	Notification notification = builder.build();

        NotificationManager notificationManager = (NotificationManager)
                context.getSystemService(Context.NOTIFICATION_SERVICE);
        notification.flags = Notification.FLAG_ONGOING_EVENT;
        notificationManager.notify(CommonUtilities.NOTIFICATION_ID_GOLE, notification);
    }



	//===========================================================================================
	// GPS測位を開始する
	//===========================================================================================
	private void startGps() {
		// 位置情報サービスの登録
		mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		if (modeGps) {
			// 位置情報の変更割り込み許可
			mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, CommonUtilities.LOCATION_MIN_TIME, CommonUtilities.LOCATION_MIN_DISTANCE, this);
		} else {
			// プロバイダの取得条件
			Criteria criteria = new Criteria();
			criteria.setAccuracy(Criteria.ACCURACY_COARSE);   // 要求精度
			criteria.setPowerRequirement(Criteria.POWER_LOW);   // 許容電力消費
			criteria.setSpeedRequired(false);               // 速度不要
			criteria.setAltitudeRequired(false);            // 高度不要
			criteria.setBearingRequired(false);            // 方位不要
			criteria.setCostAllowed(false);               // 費用の発生不可？
			// ※概ねネットワーク測位の情報を取得する
			mLocationManager.requestLocationUpdates(mLocationManager.getBestProvider(criteria, true),0,0,this);
		}
	}

	//===========================================================================================
	// GPS測位を止める
	//===========================================================================================
	private void stopGps() {
		if (mLocationManager != null) {
			// 位置情報の更新を止める (省電力対策)
			mLocationManager.removeUpdates(this);
			// 位置情報サービス登録を破棄
			mLocationManager = null;
			// 状態フラグを更新
			gpsStatus = false;
		}
	}

	//===========================================================================================
	// GPS再スタート
	//===========================================================================================
	private void restartGps() {
		// 測位停止
		stopGps();
		// 測位開始
		startGps();
	}


	//===========================================================================================
	// GPS測位結果から値を取り出す
	//===========================================================================================
	private void getLocationData() {
	   // 最後に取得した位置情報
	   Location loc;
	   if (modeGps) {
	      loc = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
	   } else {
	      loc = mLocationManager.getLastKnownLocation(mLocationManager.getBestProvider(new Criteria(), true));
	      // GPSモードへ切替
	      modeGps = true;
	      restartGps();
	   }
	   if (loc != null) {
	      // 現在日時と測位日時を比較
	      Long nowMillisecond = new Date().getTime();
	      Long locMillisecond = loc.getTime();
	      if (((nowMillisecond - locMillisecond) / 1000) > CommonUtilities.GPS_NG_TIME) {
	         // 古すぎるデータなので不採用
	         //Toast.makeText(this, "5分以上前のデータ", Toast.LENGTH_LONG).show();
	    	  modeGps = false;
	    	  return;
	      }
	      /*
	      // 前回取得誤差と最新取得誤差を比較
	      if (loc.hasAccuracy()) {
	         if (accuracy <= loc.getAccuracy()) {
	            // 精度が悪くなったので不採用
	            //Toast.makeText(this, "精度が悪い：" + loc.getAccuracy() + " m", Toast.LENGTH_LONG).show();
	            return;
	         }
	      }
	      */
	   }
	}



	@Override
	public void onProviderDisabled(String provider) {

	}
	@Override
	public void onProviderEnabled(String provider) {

	}
	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		switch (status) {
		case LocationProvider.AVAILABLE:
			Log.v("Status", "AVAILABLE");
			break;
		case LocationProvider.OUT_OF_SERVICE:
			Log.v("Status", "OUT_OF_SERVICE");
			break;
		case LocationProvider.TEMPORARILY_UNAVAILABLE:
			Log.v("Status", "TEMPORARILY_UNAVAILABLE");
			break;
		}
	}
}
